//*****************************************************************************
//
// boot_loader.c - The file holds the main control loop of the boot loader.
//
// Copyright (c) 2006-2007 Luminary Micro, Inc.  All rights reserved.
//
// Software License Agreement
//
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
//
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws.  All rights are reserved.  Any use in violation
// of the foregoing restrictions may subject the user to criminal sanctions
// under applicable laws, as well as to civil liability for the breach of the
// terms and conditions of this license.
//
// THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 323 of the Stellaris boot loader.
//
//*****************************************************************************

#include "hw_ints.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "gpio.h"
#include "hw_ssi.h"
#include "hw_uart.h"
#include "hw_nvic.h"
#include "hw_gpio.h"
#include "hw_sysctl.h"
#include "hw_flash.h"
#include "flash.h"
#include "lmicmds.h"
#include "boot_loader.h"

#include "w25x32.h"
#include "driverlib\sysctl.h"

//*****************************************************************************
//
//! \defgroup main_loop Code Documentation
//! This section describes the functions that are responsible for handling the
//! main control loop of the boot loader.
//! @{
//
//*****************************************************************************

int check_for_update_firmware();
int CheckForceUpdate(void);
void DecryptData(unsigned char *pucBuffer, unsigned char ucSize);

//*****************************************************************************
//
//! Reserves space for the boot loader's stack.
//
//*****************************************************************************
unsigned long pulStack[STACK_SIZE];

//*****************************************************************************
//
//! Holds the current status of the last command that was issued to the
//! boot loader.
//
//*****************************************************************************
unsigned char g_ucStatus;

//*****************************************************************************
//
//! This holds the current remaining size in bytes to be downloaded.
//
//*****************************************************************************
unsigned long g_ulTransferSize;

//*****************************************************************************
//
//! This holds the current address that is being written to during a download
//! command.
//
//*****************************************************************************
unsigned long g_ulTransferAddress;

//*****************************************************************************
//
//! This is the data buffer used during transfers to the boot loader.
//
//*****************************************************************************
unsigned long g_ulDataBuffer[16];

//*****************************************************************************
//
//! This is an specially aligned buffer pointer to g_ulDataBuffer to make
//! copying to the buffer simpler.  It must be offset to end on an address that
//! ends with 3.
//
//*****************************************************************************
unsigned char *g_pucDataBuffer;

//*****************************************************************************
//
//! Aligns a word on a 32-bit boundary.
//!
//! \param pucData is a pointer to the data to be converted.
//!
//! Converts an unaligned 32-bit value in memory to an aligned 32-bit value.
//!
//! \returns The converted 32-bit value.
//
//*****************************************************************************
unsigned long
AlignWord(unsigned char * pucData)
{
    //
    // Align the bytes in the buffer into a 32-bit value.
    //
    return((pucData[0] << 24) | (pucData[1] << 16) | (pucData[2] << 8) |
           (pucData[3]));
}

//*****************************************************************************
//
//! The main control loop of the boot loader.
//
//*****************************************************************************
int
main(void)
{
    unsigned char ucSize;
    unsigned long ulTemp;
    unsigned long ulFlashSize;
#ifdef AUTOBAUD
    unsigned int uiProcRatio;
#endif

    //
    // Enable clocking to GPIO Port B.
    //
    HWREG(SYSCTL_RCGC2) = SYSCTL_SET2_GPIOB | SYSCTL_SET2_GPIOA;

    //
    // This ensures proper alignment of the global buffer so that the one byte
    // size parameter used by the packetized format is easily skipped for data
    // transfers.
    //
    g_pucDataBuffer = ((unsigned char *)g_ulDataBuffer)+3;

    //
    // Insure that the COMMAND_SEND_DATA cannot be sent to erase the boot
    // loader before the application is erased.
    //
    g_ulTransferAddress = 0xffffffff;

    //
    // Set the clock source to the core clock and enable the counter.
    //
    HWREG(NVIC_ST_CTRL) = NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_ENABLE;

    //
    // Test if an update is needed or being requested, if not then just call
    // the application.
    //

    check_for_update_firmware();

    //if(CheckForceUpdate() == 0)
    {
        CallApplication(0x2000);
    }

#ifdef AUTOBAUD
    //
    // Set the reload value to the maximum, there are only 24 bits in the
    // register but loading 32 bits of ones is more efficient.
    //
    HWREG(NVIC_ST_RELOAD) = 0xffffffff;
#endif // #ifdef AUTOBAUD

    //
    // Set the flash to program at 9 MHz since that is just beyond the fastest
    // that we run.
    //
    HWREG(FLASH_USECRL) = 0x7;

#ifdef SSI_ENABLE_UPDATE
    //
    // Set the pad(s) for standard push-pull operation.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_PUR) = SSI_PINS;
    HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) = SSI_PINS;

    //
    // Configure the SSI port.
    //
    SSIConfig();
#endif // #ifdef SSI_ENABLE_UPDATE

#ifdef UART_ENABLE_UPDATE
#ifdef AUTOBAUD
    //
    // Keep attempting to sync until we are successful.
    //
    while(AutoBaud(&uiProcRatio) < 0)
    {
    }

    //
    // Configure the buad rate based on what was detected.
    //
    UARTConfig(uiProcRatio);


    //
    // Need to ack in the UART case to hold it up while we get things
    // set up.
    //
    AckPacket();
#else
    UARTConfig(UART_BAUD_RATIO(115200));
#endif
#endif // #ifdef UART_ENABLE_UPDATE


    //
    // Read any data from the serial port in use.
    //
    while(1)
    {
        //
        // Receive a packet from the port in use.
        //
        ucSize = sizeof(g_ulDataBuffer);
        if(ReceivePacket(g_pucDataBuffer, &ucSize) != 0)
        {
            continue;
        }

        //
        // The first byte of the data buffer has the command and determines
        // the format of the rest of the bytes.
        //
        switch(g_pucDataBuffer[0])
        {
            //
            // This was a simple ping command.
            //
            case COMMAND_PING:
            {
                //
                // This command always sets the status to COMMAND_RET_SUCCESS
                //
                g_ucStatus = COMMAND_RET_SUCCESS;

                //
                // Just acknowledge that the command was received.
                //
                AckPacket();

                //
                // Go back and wait for a new command.
                //
                break;
            }

            //
            // This command indicates the start of a download sequence.
            //
            case COMMAND_DOWNLOAD:
            {
                //
                // Get the address and size from the command.
                //
                g_ulTransferAddress = AlignWord(&g_pucDataBuffer[1]);
                g_ulTransferSize = AlignWord(&g_pucDataBuffer[5]);

                //
                // This determines the size of the flash available on the part
                // in use.
                //
                ulFlashSize = (0x800 +
                               ((HWREG(SYSCTL_DC0) &
                                 SYSCTL_DC0_FLASHSZ_MASK) << 11));

                //
                // Test if the size of the download is too large for the
                // device.
                //
                if((g_ulTransferAddress + g_ulTransferSize) >= ulFlashSize)
                {
                    //
                    // Setting g_ulTransferSize to zero makes COMMAND_SEND_DATA
                    // fail to accept any more data.
                    //
                    g_ulTransferSize = 0;

                    //
                    // Set the code to an error to indicate that the last
                    // command failed.  This informs the updater program that
                    // the download command failed.
                    //
                    g_ucStatus = COMMAND_RET_INVALID_ADR;
                }
                //
                // It is not valid to partially overwrite the boot loader
                // itself so fail if this is requested.
                //
                else if((g_ulTransferAddress < APP_START_ADDRESS) &&
                   (g_ulTransferAddress != 0))
                {
                    //
                    // Setting g_ulTransferSize to zero makes
                    //  COMMAND_SEND_DATA fail to accept any more data.
                    //
                    g_ulTransferSize = 0;

                    //
                    // Set the code to an error to indicate that the last
                    // command failed.  This informs the updater program
                    // that the download command failed.
                    //
                    g_ucStatus = COMMAND_RET_INVALID_ADR;
                }
                else
                {
                    //
                    // Leave the boot loader present until we start getting
                    // an image.
                    //
                    ulTemp = APP_START_ADDRESS;
                    g_ucStatus = COMMAND_RET_SUCCESS;
                    while(ulTemp < ulFlashSize)
                    {
                        //
                        // Start erasing all blocks of flash that are not in
                        // use by the boot loader.
                        //
                        if(FlashErase(ulTemp))
                        {
                            //
                            // Setting g_ulTransferSize to zero makes
                            // COMMAND_SEND_DATA fail to accept any more data.
                            //
                            g_ulTransferSize = 0;

                            //
                            // Indicate that the flash erase failed.
                            //
                            g_ucStatus = COMMAND_RET_FLASH_FAIL;
                        }

                        //
                        // Continue to the next block to erase.
                        //
                        ulTemp += 0x400;
                    }
                }

                //
                // Acknowledge that this command was received correctly.  This
                // does not indicate success, just that the command was
                // received.
                //
                AckPacket();

                //
                // Go back and wait for a new command.
                //
                break;
            }

            //
            // This command is sent to transfer data to the device following
            // a download command.
            //
            case COMMAND_SEND_DATA:
            {
                //
                // If this is overwriting the boot loader then the application
                // has already been erased so now erase the boot loader.
                //
                if(g_ulTransferAddress == 0)
                {
                    //
                    // Erase the application before the boot loader.
                    //
                    ulTemp = 0;
                    g_ucStatus = COMMAND_RET_SUCCESS;
                    while(ulTemp < APP_START_ADDRESS)
                    {
                        //
                        // Erase each block individually.
                        //
                        if(FlashErase(ulTemp))
                        {
                            //
                            // Setting g_ulTransferSize to zero makes
                            // COMMAND_SEND_DATA fail to accept any more data.
                            //
                            g_ulTransferSize = 0;

                            //
                            // Indicate that the flash erase failed.
                            //
                            g_ucStatus = COMMAND_RET_FLASH_FAIL;
                        }

                        //
                        // Continue to the next block to erase.
                        //
                        ulTemp += 0x400;
                    }
                }

                //
                // Take one byte off for the command.
                //
                ucSize = ucSize - 1;

                //
                // Check if there are any more bytes to receive.
                //
                if(g_ulTransferSize >= ucSize)
                {
                    //
                    // This function is a stub to show where to insert a
                    // function to decrypt the data as it is received.
                    //
#ifdef ENABLE_DECRYPTION
                    DecryptData(&g_pucDataBuffer[1], ucSize);
#endif

                    //
                    // Program the bytes into the flash as they are received.
                    //
                    if(FlashProgram((unsigned long *)&g_pucDataBuffer[1],
                                    g_ulTransferAddress, (ucSize + 3) & ~3))
                    {
                        //
                        // Indicate that the flash programming failed.
                        //
                        g_ucStatus = COMMAND_RET_FLASH_FAIL;
                    }
                    else
                    {
                        //
                        // Now update the address to program.
                        //
                        g_ulTransferSize -= ucSize;
                        g_ulTransferAddress += ucSize;
                    }
                }
                else
                {
                    //
                    // This indicates that too much data is being sent to the
                    // device.
                    //
                    g_ucStatus = COMMAND_RET_INVALID_ADR;
                }

                //
                // Acknowledge that this command was received correctly.  This
                // does not indicate success, just that the command was
                // received.
                //
                AckPacket();

                //
                // Go back and wait for a new command.
                //
                break;
            }

            //
            // This command just returns the status of the last command that
            // was sent.
            //
            case COMMAND_GET_STATUS:
            {
                //
                // Acknowledge that this command was received correctly.  This
                // does not indicate success, just that the command was
                // received.
                //
                AckPacket();

                //
                // Return the status to the updater.
                //
                SendPacket(&g_ucStatus, 1);

                //
                // Go back and wait for a new command.
                //
                break;
            }

            //
            // This command is used to reset the device.
            //
            case COMMAND_RESET:
            {
                //
                // Send out a one-byte ACK to ensure the byte goes back to the
                // host before we reset everything.
                //
                AckPacket();

                //
                // Perform a software reset request.  This will cause the
                // microcontroller to reset, no further code will be executed.
                //
                HWREG(NVIC_APINT) = (NVIC_APINT_VECTKEY |
                                     NVIC_APINT_SYSRESETREQ);

                //
                // The microcontroller should have reset, so this should never
                // be reached.  Just in case, loop forever.
                //
                while(1)
                {
                }
            }

            //
            // Just acknowledge the command and set the error to indicate that
            // a bad command was sent.
            //
            default:
            {
                //
                // Acknowledge that this command was received correctly.  This
                // does not indicate success, just that the command was
                // received.
                //
                AckPacket();

                //
                // Indicate that a bad comand was sent.
                //
                g_ucStatus = COMMAND_RET_UNKNOWN_CMD;

                //
                // Go back and wait for a new command.
                //
                break;
            }
        }
    }
}

//*****************************************************************************
//
//! Checks if an update is needed or is being requested.
//!
//! This function detects if an update is being requested or if there is no
//! valid code presently located on the microcontroller.  This is used to tell
//! whether or not to enter update mode.
//!
//! \returns
//!     A non-zero value if an update is needed or is being requested.
//
//*****************************************************************************
int
CheckForceUpdate(void)
{
    unsigned long ulTemp;

    //
    // This results in checking just the single bit FORCED_UPDATE_PIN on the
    // port specified by FORCED_UPDATE_PORT.  It is using the bit
    // addressability feature of the GPIO ports on Stellaris microcontrollers.
    //
#if (FORCED_UPDATE_POLARITY == 1)
    if(HWREG(FORCED_UPDATE_PORT + (1 << (FORCED_UPDATE_PIN + 2))) != 0)
    {
        return(1);
    }
#else
    if(HWREG(FORCED_UPDATE_PORT + (1 << (FORCED_UPDATE_PIN + 2))) == 0)
    {
        return(1);
    }
#endif
    ulTemp = *((unsigned long *)APP_START_ADDRESS);

    //
    // If the first location is 0xfffffffff or something that does not looks
    // like a stack pointer then return that we need to force and update.
    //
    if((ulTemp == 0xffffffff) || ((ulTemp & 0xfff00000) != 0x20000000) ||
       ((((unsigned long *)APP_START_ADDRESS)[1] & 0xfff00000) !=
        0x00000000))
    {
        return(1);
    }

    //
    // No update was needed or requested.
    //
    return(0);
}

//*****************************************************************************
//
//! Does an in place decryption of downloaded data.
//!
//! \param pucBuffer is the buffer that holds the data to decrypt.
//!
//! \param ucSize is the size, in bytes, of the buffer that was passed in via
//!     the pucBuffer parameter.
//!
//! This function is a stub that could provide in place decryption of the data
//! that is being downloaded to the device.
//!
//! \returns
//!     none.
//
//*****************************************************************************
void
DecryptData(unsigned char *pucBuffer, unsigned char ucSize)
{
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************



void init_io ()
{
	SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ);

	SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOA);
	GPIOPadConfigSet (GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD_WPU);
	GPIODirModeSet (GPIO_PORTA_BASE, GPIO_PIN_6, GPIO_DIR_MODE_IN);
	GPIODirModeSet (GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_DIR_MODE_OUT);

	SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOE);
	GPIOPadConfigSet (GPIO_PORTE_BASE, 0xff, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD_WPU);
	GPIODirModeSet (GPIO_PORTE_BASE, 0xff, GPIO_DIR_MODE_IN);

	SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOB);
	GPIOPadConfigSet (GPIO_PORTB_BASE, 0xff, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD_WPU);
	GPIODirModeSet (GPIO_PORTB_BASE, 0xff, GPIO_DIR_MODE_OUT);

	GPIOPadConfigSet (GPIO_PORTB_BASE, 0xff, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD);
	GPIODirModeSet (GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_DIR_MODE_OUT);
	GPIODirModeSet (GPIO_PORTB_BASE, GPIO_PIN_0, GPIO_DIR_MODE_IN);
	GPIODirModeSet (GPIO_PORTB_BASE, GPIO_PIN_1, GPIO_DIR_MODE_IN);
	GPIODirModeSet (GPIO_PORTB_BASE, GPIO_PIN_3, GPIO_DIR_MODE_OUT);
	GPIODirModeSet (GPIO_PORTB_BASE, GPIO_PIN_4 | GPIO_PIN_6 | GPIO_PIN_7 , GPIO_DIR_MODE_OUT);
	GPIODirModeSet (GPIO_PORTB_BASE, GPIO_PIN_5, GPIO_DIR_MODE_OUT);

	SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOD);
	GPIOPadConfigSet (GPIO_PORTD_BASE, 0xff, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD_WPU);
	GPIODirModeSet (GPIO_PORTD_BASE, 0xff, GPIO_DIR_MODE_OUT);
	GPIODirModeSet (GPIO_PORTD_BASE, GPIO_PIN_2, GPIO_DIR_MODE_IN);

	/* Configure the GPIO for the LED. */
	//SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOF);
	//GPIOPadConfigSet (GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2  | GPIO_PIN_3 , GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
	//GPIODirModeSet (GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3 , GPIO_DIR_MODE_OUT);
	//GPIODirModeSet (GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_DIR_MODE_OUT );
	//GPIOPinWrite (GPIO_PORTF_BASE, GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_2|GPIO_PIN_3);

	/* Configure UART0 for 115200 baud. */
	//SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOD);


}


#define _d_image_address_base (0*1024*1024 + 8*64*1024)
//#define _d_image_address_base (3*1024*1024 + 8*64*1024)
#define _d_image_length_address (_d_image_address_base + 0)
#define _d_image_check_sum_address (_d_image_address_base + 4)
#define _d_image_address (_d_image_address_base + 1*64*1024)


int sdk_flash_cs1_low(void)
{
	GPIOPinWrite (GPIO_PORTC_BASE, GPIO_PIN_6, 0);
	return 0;
}
int sdk_flash_cs1_high(void)
{
	GPIOPinWrite (GPIO_PORTC_BASE, GPIO_PIN_6, GPIO_PIN_6);
	return 0;
}
int sdk_flash_cs2_low(void)
{
	GPIOPinWrite (GPIO_PORTC_BASE, GPIO_PIN_7, 0);
	return 0;
}
int sdk_flash_cs2_high(void)
{
	GPIOPinWrite (GPIO_PORTC_BASE, GPIO_PIN_7, GPIO_PIN_7);
	return 0;
}

int flash_select_chip1(void){
	sdk_flash_cs1_high();
	sdk_flash_cs2_high();
	sdk_flash_cs1_low();
	return 0;
}
int flash_unselect_chip1(void){
	sdk_flash_cs1_high();
	return 0;
}

int flash_select_chip2(void){
	sdk_flash_cs1_high();
	sdk_flash_cs2_high();
	sdk_flash_cs2_low();
	return 0;
}
int flash_unselect_chip2(void){
	sdk_flash_cs2_high();
	return 0;
}

// flash
//	flash_init
void flash_init(void){
	SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOC);
	GPIOPadConfigSet (GPIO_PORTC_BASE, 0xf | GPIO_PIN_4| GPIO_PIN_6| GPIO_PIN_7, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD_WPU);
	GPIODirModeSet (GPIO_PORTC_BASE, 0xf| GPIO_PIN_6| GPIO_PIN_7, GPIO_DIR_MODE_OUT);

	flash_select_chip1();
	return ;
}

unsigned image_read_file_length()
{
	unsigned len=0;
	W25X_ReadData( _d_image_length_address , (char*)&len, 4 );
	return len;
}
unsigned image_read_file_check_sum()
{
	unsigned cs=0;
	W25X_ReadData( _d_image_check_sum_address , (char*)&cs, 4 );
	return cs;
}
int delay( int no )
{
	int i;
	for( i=0;i<no;i++ );
	return 0;
}
unsigned flash_check_sum( unsigned flash_pos, unsigned flash_size )
{
	unsigned check_sum = 0;
	void *h;
	unsigned char buf[64];
	int i;
	int size;
	int count = 0;
	unsigned char uc;

	int block_size = sizeof( buf );
	
	while( 1 ){
		if( block_size > flash_size )
			block_size = flash_size;
		W25X_ReadData( flash_pos, buf, block_size );
		size = block_size;
		flash_pos += block_size;
		flash_size -= block_size;

		//size = df_read( h, buf, sizeof( buf ) );
		if( size == 0 )
			break;
		for( i=0; i<size; i++ ){
			check_sum += (unsigned char)buf[i];
			count ++;
			//WDT_Clear();
			uc = (unsigned char)buf[i];
			//if( uc == 0xd )
			//	uc = 0x20;
			//if( uc == 0xa )
			//	uc = 0x20;
			//TRACE("%d, %d, %c %d\n", count, (unsigned char)buf[i], uc, check_sum );		
		}
		if( flash_size <= 0 )
			break;
	}
	//_d_int( count );
	//_d_int( check_sum );
	return check_sum;
}
int update_firmware()
{
	int len,tlen;
	unsigned cs, bin_cs;
	unsigned block_size;
	unsigned buf[1024/4];
	unsigned base;
	tlen = len = image_read_file_length();
	cs = image_read_file_check_sum();
	if( len >= 256 * 1024 || len == 0 || len == 0xffffffff )
		return -1;
	if( cs == 0 || cs == 0xffffffff )
		return -1;
	bin_cs = flash_check_sum( _d_image_address, len );
	if( bin_cs != cs ){
		return -1;
	}
	block_size = sizeof(buf);

	// erase
	len = tlen;
	base = 0;
	while( len > 0 ){
		if( FlashErase( base +  APP_START_ADDRESS ) ){
			while(1);
		}
		base += block_size;
		len -= block_size;
	}
	// programe
	len = tlen;
	base = 0;
	while( len > 0 ){
		W25X_ReadData( base + _d_image_address, (char*)buf, block_size );
		if( FlashProgram( (unsigned long*)buf, (base + APP_START_ADDRESS) , block_size ) ){
			while(1);
		}
		base += block_size;
		len -= block_size;
		//delay( 100000 );
	}
	// clear the image length flag.
	len = 0;					   
	w25x_program( _d_image_length_address, (char*)&len, 4 );
	return 0;
}
int check_for_update_firmware()
{
	unsigned id;
	init_io();
	spi_init();
	flash_init();
	FlashUsecSet( 8 );
  	id = W25X_ReadJEDECID();
  	if( W25Q32_JEDECID != id && W25Q64_JEDECID != id ){
		// error message :  flash chip incorrect
		//while( 1 );
	}							  
	//while( 1 );
	// SPI flash OK
	// do the update firmware action.
	update_firmware();
	return 0;
}

